/**
 * 命令式弹窗调用
 */
import { AppContext, Component, ComponentPublicInstance, createVNode, getCurrentInstance, render, VNode } from 'vue';

export interface Options {
  /** 组件显隐 */
  visible?: boolean;
  /** 组件关闭回调事件 */
  onClose?: () => void;
  /** 挂载到元素 */
  // appendTo?: HTMLElement | string;
  [key: string]: unknown;
}

export interface CommandComponent {
  (options: Options): VNode;
}

// const getAppendToElement = (props: Options): HTMLElement => {
//   let appendTo: HTMLElement | null = document.body;
//   if (props.appendTo) {
//     if (typeof props.appendTo === 'string') {
//       appendTo = document.querySelector<HTMLElement>(props.appendTo);
//     }
//     if (props.appendTo instanceof HTMLElement) {
//       appendTo = props.appendTo;
//     }
//     if (!(appendTo instanceof HTMLElement)) {
//       appendTo = document.body;
//     }
//   }
//   return appendTo;
// };

const initInstance = <T extends Component>(
  Component: T,
  props: Options,
  container: HTMLElement,
  appContext: AppContext | null = null
) => {
  const vNode = createVNode(Component, props);
  vNode.appContext = appContext;
  render(vNode, container);

  /**
   * naiveUI弹窗默认挂载到body上，
   * 此处设置无效，请直接传值绑定在弹窗组件上实现此功能,
   *  或者自己实现弹窗
   */
  // getAppendToElement(props).appendChild(container); 

  return vNode;
};

export const useCommandComponent = <T extends Component>(Component: T): CommandComponent => {
  const appContext = getCurrentInstance()?.appContext;
  /** 补丁：Component中获取当前组件树的provides, 注意这里只能获取到调用hook前定义的provides */
  if (appContext) {
    const currentProvides = (getCurrentInstance() as any)?.provides;
    Reflect.set(appContext, 'provides', {...appContext.provides, ...currentProvides});
  }
  
  const container = document.createElement('div');

  const close = () => {
    render(null, container);
    container.parentNode?.removeChild(container);
  };

  const CommandComponent = (options: Options): VNode => {
    if (!Reflect.has(options, 'visible')) {
      options.visible = true;
    }
    if (typeof options.onClose !== 'function') {
      /** 若未传入关闭回调事件，直接关闭弹窗 */
      options.onClose = close;
    } else {
      const originOnClose = options.onClose;
      options.onClose = () => {
         /** 若传入关闭回调事件，则先执行回调，再关闭弹窗 */
        originOnClose();
        close();
      };
    }
    const vNode = initInstance<T>(Component, options, container, appContext);
    const vm = vNode.component?.proxy as ComponentPublicInstance<Options>;
    for (const prop in options) {
      if (!Reflect.has(vm.$props, prop)) {
        vm[prop as keyof ComponentPublicInstance] = options[prop];
      }
    }
    return vNode;
  };

  return CommandComponent;
};

export default useCommandComponent;
